package com.oceantrader.root.tools;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * SQL语句
 */
public class Sql {
    private final ArrayList<String> sqls = new ArrayList<>();
    private final HashMap<String, Object> params = new HashMap<>();
    private int paramIndex = 0;
    private static final String PARAM_PREFIX = "P_";

    /**
     * 向后拼接SQL语句，会和之前的语句自动使用空格隔开
     * 
     * @param flag 是否拼接
     * @param sql  SQL内容，其中#{param}会识别为参数
     * @return chain链式调用
     */
    public Sql append(boolean flag, String sql) {
        if (flag) {
            this.append(sql);
        }
        return this;
    }

    /**
     * 向后拼接SQL语句，会和之前的语句自动使用空格隔开
     * 
     * @param sql SQL内容，其中#{param}会识别为参数
     * @return chain链式调用
     */
    public Sql append(String sql) {
        this.sqls.add(sql);
        return this;
    }

    /**
     * 向后拼接SQL语句，会和之前的语句自动使用空格隔开
     * 
     * @param flag  是否拼接
     * @param sql   SQL内容，其中#{}会识别为参数
     * @param param 替换#{}的参数
     * @return chain链式调用
     */
    public Sql append(boolean flag, String sql, Object... params) {
        if (flag) {
            this.append(sql, params);
        }
        return this;
    }

    /**
     * 向后拼接SQL语句，会和之前的语句自动使用空格隔开
     * 
     * @param sql   SQL内容，其中#{}会识别为参数
     * @param param 替换#{}的参数
     * @return chain链式调用
     */
    public Sql append(String sql, Object... params) {
        for (Object param : params) {
            int index = sql.indexOf("#{}");
            if (index != -1) {
                String key = PARAM_PREFIX + (paramIndex++);
                sql = sql.replaceFirst("#\\{\\}", "#{" + key + "}");
                this.params.put(key, param);
            }
        }
        this.sqls.add(sql);
        return this;
    }

    /**
     * 设置参数，例如想要替换语句中的#{param}，此方法需要.setParam("param", value);
     * 
     * @param key   参数名
     * @param param 参数值
     * @return chain链式调用
     */
    public Sql setParam(String key, Object param) {
        this.params.put(key, param);
        return this;
    }

    /**
     * 设置参数
     * 
     * @param params 参数集合
     * @return chain链式调用
     */
    public Sql setParams(Map<String, ? extends Object> params) {
        this.params.putAll(params);
        return this;
    }

    /**
     * 获取参数集合
     * 
     * @return 参数集合
     */
    public HashMap<String, Object> getParams() {
        return this.params;
    }

    /**
     * 向前 或 向后 忽略掉指定的字符（区分大小写！）
     * 
     * <pre>
     * sql
     *         .append("SELECT * FROM user WHERE").ignore("AND")// 向后忽略掉AND
     *         .append("AND id = #{id}")
     *         .append("AND name = #{name}");
     * </pre>
     * 
     * <pre>
     * sql
     *         .append("UPDATE user SET")
     *         .append("nick = #{nick},")
     *         .append("name = #{name},")
     *         .ignore(",")// 向前忽略逗号
     *         .append("WHERE id = #{id}");
     * </pre>
     * 
     * @param sql 被忽略的SQL语句
     * @return chain链式调用
     */
    public Sql ignore(String sql) {
        if (sql != null && !sql.trim().equals("")) {
            this.sqls.add("[ignore]" + sql);
        }
        return this;
    }

    /**
     * 循环拼接SQL语句
     * 
     * @param begin   开始字符
     * @param sqlList 内容集合
     * @param split   分隔符
     * @param end     结束字符
     * @return chain链式调用
     */
    public Sql forEach(String begin, Collection<String> sqlList, String split, String end) {
        String center = sqlList.stream().collect(Collectors.joining(split));
        this.sqls.add(begin);
        this.sqls.add(center);
        this.sqls.add(end);
        return this;
    }

    /**
     * 循环拼接参数内容：#{param1}, #{param2}, #{param3}...
     * 
     * @param begin  开始字符
     * @param params 参数集合
     * @param split  分隔符
     * @param end    结束字符
     * @return chain链式调用
     */
    public Sql forEachParams(String begin, Collection<? extends Object> params, String split, String end) {
        ArrayList<String> paramSqls = new ArrayList<>();
        for (Object param : params) {
            String key = PARAM_PREFIX + (paramIndex++);
            paramSqls.add("#{" + key + "}");
            this.params.put(key, param);
        }
        this.sqls.add(begin);
        this.sqls.add(paramSqls.stream().collect(Collectors.joining(split)));
        this.sqls.add(end);
        return this;
    }

    /**
     * 输出当前SQL，含参数占位符：#{param1}, #{param2}, #{param3}...
     * 
     * @return 当前SQL
     */
    public String toString() {
        StringBuilder result = new StringBuilder();
        i: for (int i = 0; i < this.sqls.size(); i++) {
            String sql = this.sqls.get(i);
            if (sql.startsWith("[ignore]")) {
                sql = sql.substring(8);
                for (int j = i - 1; j > 0; j--) {
                    if (this.sqls.get(j) != null && !this.sqls.get(j).startsWith("[ignore]")) {
                        String beforeSql = this.sqls.get(j).trim();
                        if (beforeSql.endsWith(sql)) {
                            beforeSql = beforeSql.substring(0, beforeSql.length() - sql.length());
                            this.sqls.set(j, beforeSql);
                            continue i;
                        }
                    }
                }
                for (int j = i + 1; j < this.sqls.size(); j++) {
                    if (this.sqls.get(j) != null) {
                        String afterSql = this.sqls.get(j).trim();
                        if (afterSql.startsWith(sql)) {
                            afterSql = afterSql.substring(sql.length());
                            this.sqls.set(j, afterSql);
                            continue i;
                        }
                    }
                }
            }
        }
        for (String sql : this.sqls) {
            if (sql.startsWith("[ignore]")) {
                continue;
            } else if (sql != null) {
                result.append(" ").append(sql);
            }
        }
        return result.toString().trim().replaceAll("\\s+", " ");
    }

    /**
     * 【仅限MySQL】用户输入的字符转为Like字符：<br>
     * 1.去除两边空格<br>
     * 2.将_%转义<br>
     * 3.将空格设置为%<br>
     * <br>
     * 此方法决定like查询的匹配习惯
     * 
     * @param column 用户输入的模糊查询条件
     * @return 输出的like查询
     */
    public static String like(String column) {
        if (column == null || column.trim().equals("")) {
            return null;
        }
        return column.trim().replaceAll("\\\\", "\\\\\\\\").replaceAll("_", "\\\\_").replaceAll("%", "\\\\%").replaceAll(" +", "%");
    }

    /**
     * 左侧%的like条件，不使用索引，不推荐！
     * 
     * @param column 用户输入的模糊查询条件
     * @return 输出的like查询
     */
    public static String likeL(String column) {
        if (column == null || column.trim().equals("")) {
            return null;
        }
        return "%" + like(column);
    }

    /**
     * 右侧%的like条件
     * 
     * @param column 用户输入的模糊查询条件
     * @return 输出的like查询
     */
    public static String likeR(String column) {
        if (column == null || column.trim().equals("")) {
            return null;
        }
        return like(column) + "%";
    }

    /**
     * 左右侧%的like条件，不使用索引，不推荐！
     * 
     * @param column 用户输入的模糊查询条件
     * @return 输出的like查询
     */
    public static String likeLR(String column) {
        if (column == null || column.trim().equals("")) {
            return null;
        }
        return "%" + like(column) + "%";
    }
}
